package org.purl.net.sparql.geo;

import java.util.List;

import com.hp.hpl.jena.sparql.ARQInternalErrorException;
import com.hp.hpl.jena.sparql.expr.ExprEvalException;
import com.hp.hpl.jena.sparql.expr.ExprList;
import com.hp.hpl.jena.sparql.expr.NodeValue;
import com.hp.hpl.jena.sparql.function.FunctionBase;
import com.hp.hpl.jena.sparql.util.Utils;

/**
 * A SPARQL Extension function that attempts to determine 
 * whether a given point is within a bounding box.
 * 
 * This simple implementation accepts 6 literal values. These 
 * describe three points using separate latitude and longitude 
 * values.
 * 
 * The first two points are the minimum and maximum longitude 
 * and latitude for the bounding box. The third is the point is 
 * being tested for containment in that box. The function returns 
 * a true or false value depending on whether the point is within 
 * the box.
 * 
 * 
 * @author ldodds
 */
public class PointInBoundingBox extends FunctionBase {

	public NodeValue exec(List args) {
        if ( args == null )
            // The contract on the function interface is that this should not happen.
            throw new ARQInternalErrorException(Utils.className(this)+": Null args list") ;
        
        if ( args.size() != 6 )
            throw new ExprEvalException(Utils.className(this)+": Wrong number of arguments: Wanted 1, got "+args.size()) ;
        
        NodeValue v1 = (NodeValue)args.get(0) ;
        NodeValue v2 = (NodeValue)args.get(1) ;
        NodeValue v3 = (NodeValue)args.get(2) ;
        NodeValue v4 = (NodeValue)args.get(3) ;
        NodeValue v5 = (NodeValue)args.get(4) ;
        NodeValue v6 = (NodeValue)args.get(5) ;
        
        return exec(v1, v2, v3, v4, v5, v6) ;
	}

	public void checkBuild(String arg0, ExprList args) {
        if ( args.size() != 6 )
            throw new ExprEvalException("Function '"+Utils.className(this)+"' takes four arguments") ;
	}

    public NodeValue exec(NodeValue minlat, NodeValue minlong, NodeValue maxlat, NodeValue maxlong, NodeValue lat, NodeValue lon)
    {
    	double mlat = getDouble(minlat);
    	double mlong = getDouble(minlong);
    	
    	double maxl = getDouble(maxlat);
    	double maxlon = getDouble(maxlong);
    	
    	double pointlat = getDouble(lat);
    	double pointlon = getDouble(lon);
    	
    	if (mlat > maxl || mlong > mlong)
    	{
    		throw new ExprEvalException(Utils.className(this)+
    					": Precondition failed minimum point greater than maximum point");
    	}
    	if (mlat < pointlat && maxl > pointlat)
    	{
    		if (mlong < pointlon && maxlon > pointlon)
    		{
    			return NodeValue.TRUE;
    		}
    	}
    
    	return NodeValue.FALSE;
    }
    
	private double getDouble(NodeValue value)
	{
		if (value.isDouble() )
		{
			return value.getDouble();
		}
		else
		{
			return Double.parseDouble( value.asString() );
		}
	}
    
}
